#!/usr/bin/env -S node -r ./livescript-transform-implicit-async/register

require! <[
  path
  crypto
  msgpack5
  caller-id
  chalk
]>
require! {
  \fs-extra : fs
  \js-yaml : yaml
}

msgpack = msgpack5!
CACHE_ROOT = ".git/cache"
HASH_ROOT = path.join CACHE_ROOT, \hash
STAT_ROOT = path.join CACHE_ROOT, \stat

#TODO 根据大小、更新时间来判断是否cached

class File
  (@cwd)->

  read:(filepath, encoding)->
    if encoding==undefined
      encoding = \utf-8
    fs.readFile(
      path.join(@cwd, filepath)
      encoding
    )

  write:(filepath, data)->
    fs.outputFile(
      path.join @cwd, filepath
      data
    )

  hash:(filepath)~>>
    hash = hasher!
    hash.update await @read(filepath,null)
    hash.digest!

  msgpack-decode : (filepath)~>>
    try
      cache = await @read(filepath, null)
      cache = msgpack.decode(cache)
      return cache
    catch e
      if e.errno != -2
        throw e
    return

  trace:->
    {filePath} = caller-id.get-data()
    trace = new Trace @, filePath
    return trace

  exists : (filepath)->
    fs.exists(path.join(@cwd, filepath))

  stat : (filepath)->
    if await @exists(filepath)
      return fs.stat(path.join(@cwd, filepath))

  stat-key : (filepath)->
      s = await @stat(filepath)
      if not s
        return
      buf = Buffer.allocUnsafe(12)
      buf.writeUIntBE s.size , 0, 6
      buf.writeUIntBE parseInt(s.mtimeMs*10000), 6, 6
      return buf


hashname='sha256'
hasher = ~>
  crypto.createHash(hashname)

_DIR = path.dirname __dirname.slice(0,-24)

class Trace
  (@file,key) ->
    @key = key.slice(_DIR.length)
    @_cache = {}

  save : ->>
    stat = {}
    li = []
    get = (k)!~>>
      r = await @file.stat-key k
      if r != undefined
        stat[k] = r

    for k,_ of @_cache
      li.push get k

    await Promise.all(li)
    @file.write(
      path.join STAT_ROOT, @key
      msgpack.encode(stat)
    )
    @file.write(
      path.join HASH_ROOT, @key
      msgpack.encode(@_cache)
    )

  write:(filepath, data)->
    console.log chalk.gray "生成 #filepath"
    hash = hasher!
    hash.update(data)
    @_cache[filepath] = hash.digest!
    @file.write filepath, data

  read:(filepath, encoding)->
    if encoding==undefined
      encoding = \utf-8
    hash = hasher!
    data = await @file.read filepath, encoding
    hash.update(data)
    @_cache[filepath] = hash.digest!
    return data

  read-yaml:(filepath)->
    try
      t = await @read "#filepath.yaml"
      return yaml.load t
    catch e
      p = path.join @file.cwd, filepath
      console.error "加载 #p 出错"
      console.error e
      return

  cached: ->
    cache = (
      await @file.msgpack-decode(
        path.join HASH_ROOT, @key
      )
    ) or {}
    stat = (
      await @file.msgpack-decode(
        path.join STAT_ROOT, @key
      )
    ) or {}
    count = 0
    for k,v of cache
      ++ count
      if not await @file.exists(k)
        console.log chalk.yellow("#k 未生成")
        return false
      t = stat[k]
      if t and t.compare(await @file.stat-key(k)) == 0
        continue
      if v.compare(await @file.hash(k))!=0
        console.log chalk.green("#k 已修改")
        return false
    if not count
      return false
    return true

  cache : (filepath)->
    @read(filepath, null)


module.exports = (cwd)~>
  new File(cwd)
